package com.smzd.framework.sso;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.savedrequest.SavedCookie;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

public class SessionBasedRedirectRequest implements SavedRequest {
    private static final String URL_PATTERN = "^((\\w+)\\:\\/\\/([\\w\\.-]+)(:(\\d+))?(\\/.*?))(\\?(.+))?$";
    private static final long serialVersionUID = -6396931261178124361L;
    private static PortResolver portResolver = new PortResolverImpl();

    protected static final Log logger = LogFactory.getLog(SessionBasedRedirectRequest.class);

    private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
    private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";

    // ~ Instance fields
    // ================================================================================================

    private final ArrayList<SavedCookie> cookies = new ArrayList<SavedCookie>();
    private final ArrayList<Locale> locales = new ArrayList<Locale>();
    private final Map<String, List<String>> headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
    private final Map<String, String[]> parameters = new TreeMap<String, String[]>(String.CASE_INSENSITIVE_ORDER);
    private final String contextPath;
    private final String method;
    private final String pathInfo;
    private final String queryString;
    private final String requestURI;
    private final String requestURL;
    private final String scheme;
    private final String serverName;
    private final String servletPath;
    private final int serverPort;

    // ~ Constructors
    // ===================================================================================================

    @SuppressWarnings("unchecked")
    public SessionBasedRedirectRequest(HttpServletRequest request, String redirect_uri) {
        Assert.notNull(request, "Request required");
        Assert.notNull(redirect_uri, "redirect_uri required");

        // Cookies
        Cookie[] cookies = request.getCookies();

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                this.addCookie(cookie);
            }
        }

        // Headers
        Enumeration<String> names = request.getHeaderNames();

        while (names.hasMoreElements()) {
            String name = names.nextElement();
            // Skip If-Modified-Since and If-None-Match header. SEC-1412,
            // SEC-1624.
            if (HEADER_IF_MODIFIED_SINCE.equalsIgnoreCase(name) || HEADER_IF_NONE_MATCH.equalsIgnoreCase(name)) {
                continue;
            }
            Enumeration<String> values = request.getHeaders(name);

            while (values.hasMoreElements()) {
                this.addHeader(name, values.nextElement());
            }
        }

        // Locales
        Enumeration<Locale> locales = request.getLocales();

        while (locales.hasMoreElements()) {
            Locale locale = (Locale) locales.nextElement();
            this.addLocale(locale);
        }

        this.method = "GET";
        this.pathInfo = null;
        if (redirect_uri.startsWith("/")) {// local
            // Primitives
            this.serverPort = portResolver.getServerPort(request);
            this.scheme = request.getScheme();
            this.serverName = request.getServerName();
            this.contextPath = request.getContextPath();
            this.servletPath = request.getServletPath();

            String base = contextPath;
            if (base.length() == 1) {
                base = "";
            }
            redirect_uri = base + redirect_uri;

            int index = redirect_uri.indexOf("?");
            if (index > 0) {
                this.queryString = redirect_uri.substring(index + 1);
                this.requestURI = redirect_uri.substring(0, index);
            } else {
                this.requestURI = redirect_uri;
                this.queryString = null;
            }

            StringBuffer sb = new StringBuffer();
            sb.append(scheme).append("://").append(serverName);
            if (serverPort != 80) {
                sb.append(":").append(serverPort);
            }
            sb.append(requestURI);
            this.requestURL = sb.toString();

        } else {// http://www.baidu.com/s?a=a&b=b
            Pattern pattern = Pattern.compile(URL_PATTERN);
            Matcher matcher = pattern.matcher(redirect_uri);
            if (matcher.matches()) {
                System.out.println("matches = '" + matcher.group(1) + "'");
                System.out.println("scheme = '" + matcher.group(2) + "'");
                System.out.println("servername = '" + matcher.group(3) + "'");
                System.out.println("serverport = '" + matcher.group(5) + "'");
                System.out.println("path = '" + matcher.group(6) + "'");
                System.out.println("query = '" + matcher.group(8) + "'");

                // Primitives
                this.requestURL = matcher.group(1);
                this.scheme = matcher.group(2);
                this.serverName = matcher.group(3);
                this.serverPort = matcher.group(5) == null ? 80 : Integer.parseInt(matcher.group(5));
                this.contextPath = "/";
                String path = matcher.group(6);
                if (path == null) {
                    path = "";
                }
                this.servletPath = path;
                this.requestURI = servletPath;
                this.queryString = matcher.group(8);
            } else {
                throw new RuntimeException("Bad URL! url='" + redirect_uri + "'");
            }
        }

        // Parameters
        if (queryString != null) {
            Map<String, List<String>> parameters = new HashMap<String, List<String>>();
            String[] segments = queryString.split("&");
            for (String segment : segments) {
                String[] kv = segment.split("=");
                List<String> values = parameters.get(kv[0]);
                if (values == null) {
                    values = new ArrayList<>();
                    parameters.put(kv[0], values);
                }
                values.add(kv[1]);
            }
            if (parameters.size() > 0) {
                for (String key : parameters.keySet()) {
                    List<String> values = parameters.get(key);
                    String[] arr = null;
                    if (values != null && values.size() > 0) {
                        arr = new String[values.size()];
                        values.toArray(arr);
                    }
                    this.addParameter(key, arr);
                }
            }
        }
    }

    // ~ Methods
    // ========================================================================================================

    private void addCookie(Cookie cookie) {
        cookies.add(new SavedCookie(cookie));
    }

    private void addHeader(String name, String value) {
        List<String> values = headers.get(name);

        if (values == null) {
            values = new ArrayList<String>();
            headers.put(name, values);
        }

        values.add(value);
    }

    private void addLocale(Locale locale) {
        locales.add(locale);
    }

    private void addParameter(String name, String[] values) {
        parameters.put(name, values);
    }

    /**
     * Determines if the current request matches the
     * <code>SessionBasedRedirectRequest</code>.
     * <p>
     * All URL arguments are considered but not cookies, locales, headers or
     * parameters.
     *
     * @param request
     *            the actual request to be matched against this one
     * @param portResolver
     *            used to obtain the server port of the request
     * @return true if the request is deemed to match this one.
     *
     */
    public boolean doesRequestMatch(HttpServletRequest request, PortResolver portResolver) {
        return true;
    }

    public String getContextPath() {
        return contextPath;
    }

    public List<Cookie> getCookies() {
        List<Cookie> cookieList = new ArrayList<Cookie>(cookies.size());

        for (SavedCookie savedCookie : cookies) {
            cookieList.add(savedCookie.getCookie());
        }

        return cookieList;
    }

    /**
     * Indicates the URL that the user agent used for this request.
     *
     * @return the full URL of this request
     */
    public String getRedirectUrl() {
        return UrlUtils.buildFullRequestUrl(scheme, serverName, serverPort, requestURI, queryString);
    }

    public Collection<String> getHeaderNames() {
        return headers.keySet();
    }

    public List<String> getHeaderValues(String name) {
        List<String> values = headers.get(name);

        if (values == null) {
            return Collections.emptyList();
        }

        return values;
    }

    public List<Locale> getLocales() {
        return locales;
    }

    public String getMethod() {
        return method;
    }

    public Map<String, String[]> getParameterMap() {
        return parameters;
    }

    public Collection<String> getParameterNames() {
        return parameters.keySet();
    }

    public String[] getParameterValues(String name) {
        return parameters.get(name);
    }

    public String getPathInfo() {
        return pathInfo;
    }

    public String getQueryString() {
        return (this.queryString);
    }

    public String getRequestURI() {
        return (this.requestURI);
    }

    public String getRequestURL() {
        return requestURL;
    }

    public String getScheme() {
        return scheme;
    }

    public String getServerName() {
        return serverName;
    }

    public int getServerPort() {
        return serverPort;
    }

    public String getServletPath() {
        return servletPath;
    }

    public String toString() {
        return "SessionBasedRedirectRequest[" + getRedirectUrl() + "]";
    }

    public static void main(String[] args) {
        String redirect_uri = "mysnaptee://www.bind.com/a";
        Pattern pattern = Pattern.compile(URL_PATTERN);
        Matcher matcher = pattern.matcher(redirect_uri);
        if (matcher.matches()) {
            System.out.println("matches = '" + matcher.group(1) + "'");
            System.out.println("scheme = '" + matcher.group(2) + "'");
            System.out.println("servername = '" + matcher.group(3) + "'");
            System.out.println("serverport = '" + matcher.group(5) + "'");
            System.out.println("query = '" + matcher.group(8) + "'");
            String path = matcher.group(6);
            if (path == null) {
                path = "";
            }
            System.out.println("path = '" + path + "'");

        } else {
            System.out.println("Bad URL! url='" + redirect_uri + "'");
        }
    }
}
